package bjut.zrpc.core.optimization.proctol;

import bjut.zrpc.core.optimization.constant.ZRpcConstant;
import bjut.zrpc.core.optimization.enums.ZRpcCompressEnum;
import bjut.zrpc.core.optimization.enums.ZRpcSerializerEnum;
import bjut.zrpc.core.optimization.extension.ExtensionLoader;
import bjut.zrpc.core.optimization.interfaces.Compress;
import bjut.zrpc.core.optimization.interfaces.Serializer;
import bjut.zrpc.core.optimization.message.ZRpcMessage;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import lombok.extern.slf4j.Slf4j;


import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Author: zhuzhiming
 * @Date: 2022/6/24 0:01
 */
@Slf4j
public class MessageEncoder extends MessageToByteEncoder<ZRpcMessage> {

    private static final AtomicInteger ID = new AtomicInteger(0);

    @Override
    protected void encode(ChannelHandlerContext context, ZRpcMessage zRpcMessage, ByteBuf byteBuf) throws Exception {

        try {
            byte messageType = zRpcMessage.getMessageType();
            byte codec = zRpcMessage.getCodec();
            byte compressCode = zRpcMessage.getCompress();

            byteBuf.writeBytes(ZRpcConstant.MAGIC_NUMBER);
            byteBuf.writeByte(ZRpcConstant.VERSION);
            byteBuf.writerIndex(byteBuf.writerIndex() + 4);
            byteBuf.writeByte(messageType);
            byteBuf.writeByte(codec);
            byteBuf.writeByte(compressCode);
            byteBuf.writeInt(ID.getAndIncrement());

            int fullLength = ZRpcConstant.HEAD_LENGTH;
            byte[] bodyBytes = null;

            if (messageType != ZRpcConstant.HEART_REQUEST_TYPE && messageType != ZRpcConstant.HEART_RESPONSE_TYPE) {
                Object bodyData = zRpcMessage.getBodyData();

                String serializationType = ZRpcSerializerEnum.getSerializationName(codec);
                Serializer serializer = ExtensionLoader.getExtensionLoader(Serializer.class).getExtension(serializationType);
                byte[] bodySerializer = serializer.serialize(bodyData);

                String compressType = ZRpcCompressEnum.getCompressName(compressCode);
                Compress compress = ExtensionLoader.getExtensionLoader(Compress.class).getExtension(compressType);

                bodyBytes = compress.compress(bodySerializer);

                fullLength += bodyBytes.length;

            }

            if (bodyBytes != null) {
                byteBuf.writeBytes(bodyBytes);
            }

            int writerIndex = byteBuf.writerIndex();

            byteBuf.writerIndex(writerIndex - fullLength + ZRpcConstant.MAGIC_NUMBER.length + ZRpcConstant.VERSION);
            byteBuf.writeInt(fullLength);

            byteBuf.writerIndex(writerIndex);
        } catch (Exception e) {
          throw new RuntimeException(e.getMessage());
        }


    }
}
